#pragma once
#include<iostream>
#include<atomic>
using namespace std;

//智能指针默认是以delete的方式删除资源
template<class T>
struct Delete //智能指针管理单个元素
{
	void operator()(T* ptr)
	{
		if (ptr != nullptr)
		{
			cout << "struct Delete~" << endl;
			delete ptr;
		}
	}
};

template<class T>
struct DeleteArray  //智能指针管理数组
{
	void operator()(T* ptr)
	{
		if (ptr != nullptr)
		{
			cout << "struct DeleteArray" << endl;
			delete[] ptr;
		}
	}
};

template<class T>
struct DeleteFile //智能指针管理的是文件
{
	void operator()(T* fp)
	{
		cout << "struct DeleteFile" << endl;
		fclose(fp);
	}
};


namespace Mango
{
	template<class T>
	class  RefCnt
	{
	public:
		RefCnt(T* ptr = nullptr) :_Ptr(ptr)
		{
			if (_Ptr != nullptr)
			{
				_Uses = 1;
				_Weaks = 1;
			}
		}
		~RefCnt() // 管理的资源不由引用计数类负责释放
		{
			cout << "~RefCnt()" << endl;
		}
		void IncrUses()
		{
			_Uses += 1;
		}
		void IncrWeaks()
		{
			_Weaks += 1;
		}
	public:
		T* _Ptr;//管理的资源
		atomic<int> _Uses;//有多少个强类型指针指向该资源
		atomic<int> _Weaks;//有多少个弱类型指针指向该资源
	};


	template<class T, class Del = Delete<T>>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr) :_Ptr(ptr)
		{
			_Ref = new RefCnt<T>(ptr);
		}
		~shared_ptr()
		{
			if (_Ptr != nullptr && --_Ref->_Uses == 0) //管理的资源不为空 && 当前对象是最后一个管理当前资源的 那么就需要负责释放该资源
			{
				_Del(_Ptr);
				if (--_Ref->_Weaks == 0) //只有当前资源的弱类型指针计数为0的时候才能删除当前对象当中的引用计数类成员
				{
					delete _Ref;
				}
				//错误赋值方式: _Ptr = _Ref = nullptr;//因为_Ref和_Ptr的类型不符合,所以不能连续赋值
				_Ptr = nullptr; //放在内部 而非外部！
				_Ref = nullptr; 
			}
		}

		//拷贝构造 => 多了一个强智能指针管理当前资源,强引用计数++  s1 = s2
		shared_ptr(const shared_ptr<T>& sp) :_Ptr(sp._Ptr), _Ref(sp._Ref)
		{
			if (_Ptr != nullptr)
			{
				_Ref->IncrUses();
			}
		}

		//移动构造  => 当前资源的强引用计数不变  s1 = move(s2)
		shared_ptr(const shared_ptr<T>&& sp) :_Ptr(sp._Ptr), _Ref(sp._Ref)
		{
			sp._Ptr = nullptr; //由于sp的资源已经移动,切记需要让指针置为空
			sp._Ref = nullptr;
		}

		//赋值重载 => 多了一个强智能指针管理当前资源,强引用计数++
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (_Ptr == sp._Ptr) //s2 = s1 或者s1 = s1 的情况都可以处理,二者指向同一个资源的话就不处理
				return *this;
			//当前对象不再管理当前资源
			this->~shared_ptr();

			//当前对象和sp对象管理同一块资源
			_Ptr = sp._Ptr;
			_Ref = sp._Ref;
			if (_Ref != nullptr) //如果管理的资源不为空,那么就增加引用计数
			{
				_Ref->IncrUses();
			}
			return *this;
		}

		//移动赋值 => 当前资源的强引用计数不变
		shared_ptr<T>& operator=(const shared_ptr<T>&& sp)
		{
			//case1：传入的是同一个对象
			if (this == &sp) // s1 = move(s1)的情况,此时不处理
				return *this;

			//case2：s1和s2管理同一块资源 s1 = move(s2) / s2 = move(s1)情况
			if (_Ptr != nullptr && sp._Ptr != nullptr && _Ptr == sp._Ptr)
			{
				//此时减少强引用计数的数量 并且让sp对象管理的资源为空
				_Ref->_Uses -= 1;
				sp._Ptr = sp._Ref = nullptr;
				return *this;
			}

			//case3：s1和s2管理的不是同一块资源 s1 = move(s2) / s2 = move(s1)
			this->~shared_ptr(); //当前对象不再管理当前这块资源
			_Ptr = sp._Ptr;
			_Ref = sp._Ref;
			sp._Ptr = nullptr;
			sp._Ref = nullptr; //切记需要让被转移资源的对象所管理的资源赋值为空
			return *this;
		}

		T* get() const
		{
			return _Ptr;
		}
		T& operator*() const
		{
			return *_Ptr;
		}
		T* operator->() const
		{
			return _Ptr;
		}
		//返回有多少个强智能指针在管理当前资源
		int use_count() const
		{
			if (_Ref == nullptr)
				return 0;
			else
				return _Ref->_Uses;
		}
		//交换当前两个强智能指针管理的资源
		void swap(shared_ptr<T>& sp)
		{
			::swap(_Ptr, sp._Ptr);
			::swap(_Ref, sp._Ref);
		}

		//方便直接使用shared_ptr对象做判断  shared_ptr<int> s1(new int(1));  if(s1) {....}
		operator bool() const
		{
			return _Ptr != nullptr;
		}

	private:
		T* _Ptr = nullptr;
		RefCnt<T>* _Ref = nullptr;
		Del _Del;

		//weak_ptr是shared_ptr的友元类,可以访问该类的私有成员
		template<class T>
		friend class weak_ptr;
	};

	template<class T>
	class weak_ptr
	{
	public:
		//无参构造
		weak_ptr() :_Ref(nullptr)
		{}

		//利用shared_ptr来进行拷贝构造
		weak_ptr(const shared_ptr<T>& sp) :_Ref(nullptr)
		{
			_Ref = sp._Ref;
			if (_Ref != nullptr)
				_Ref->IncrWeaks();//增加弱引用计数数量
		}


		//利用weak_ptr进行拷贝构造
		/*
			//析构的时候强智能指针要析构强引用计数个数，也析构弱引用计数个数
			//弱引用计数只能析构计数结构，不能析构对象
		*/
		weak_ptr(const weak_ptr<T>& wp) :_Ref(nullptr)
		{
			_Ref = wp._Ref;
			if (_Ref != nullptr)
				_Ref->IncrWeaks();//增加弱引用计数数量
		}

		//移动构造 => 弱引用计数数量不变 
		weak_ptr(const weak_ptr<T>&& wp) :_Ref(nullptr)
		{
			_Ref = wp._Ref;
		}

		//使用weak_ptr对象来对weak_ptr对象进行赋值重载
		weak_ptr<T>& operator=(const weak_ptr<T>& wp)
		{
			//当前两个弱智能指针监控的是同一个资源,不处理
			if (wp._Ref == _Ref) return *this;
			//监控的不是同一个资源
			this->~weak_ptr(); //当前弱类型指针对象不再监控当前资源
			_Ref = wp._Ref;
			if (_Ref != nullptr)
			{
				_Ref->IncrWeaks();//增加弱引用计数数量
			}
			return *this;
		}

		//使用shared_ptr来对weak_ptr对象进行赋值重载
		weak_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			this->~weak_ptr(); //当前弱类型指针对象不再监控当前资源
			_Ref = sp._Ref;//由于weak_ptr是shared_ptr的友元类,所以可以访问其私有成员
			if (_Ref != nullptr)
			{
				_Ref->IncrWeaks();//增加弱引用计数
			}
			return *this;
		}

		//移动赋值 =>  弱引用计数数量不变 
		weak_ptr<T>& operator=(const weak_ptr<T>&& wp)
		{
			//case1：传入的是相同的对象
			if (this == &wp) return *this;
			//case2：当前两个弱类型指针监控的是同一个资源
			if (_Ref != nullptr && wp._Ref != nullptr && _Ref == wp._Ref)
			{
				_Ref->_Weaks--;//弱引用计数数量-1
				wp._Ref = nullptr; //wp对象不再监控该资源
				return *this;
			}
			//case3:当前两个弱类型指针监控的是不同一个资源
			this->~weak_ptr();//当前对象不再监控当前资源
			_Ref = wp._Ref;
			wp._Ref = nullptr;  //wp对象不再监控该资源
			return *this;
		}

		//注意：不能使用shared_ptr对weak_ptr对象进行移动赋值
		//原因：移动赋值要把资源赋值给弱指针，但弱智能指针不拥有资源,弱智能指针只是起监控的作用
		//	   引用检测引用计数RefCnt结构是否存在，解决环形引用
		weak_ptr<T>& operator=(shared_ptr<T>&& sp) = delete;

		~weak_ptr()
		{
			if (_Ref != nullptr) //当前对象确实监控着资源
			{
				if (--_Ref->_Weaks == 0) //当前对象是最后一个监控该资源的对象
				{
					delete _Ref;
					_Ref = nullptr;//切记需要让_Ref置为空指针
				}
			}
		}

		//检测被监控的对象是否被还有效  无效为true
		bool expired() const
		{
			return _Ref->_Uses == 0; //如果当前强引用计数为0,说明已经无效
		}

		//加锁 => 将weak_ptr提升为shared_ptr
		shared_ptr<T> lock() const
		{
			shared_ptr<T> tmp;
			tmp._Ref = _Ref;
			tmp._Ptr = _Ref->_Ptr; //引用计数类当中有管理的资源指针！
			_Ref->IncrUses();//增加强引用计数
			return tmp;//返回的是临时对象,所以不能传引用返回
		}
	private:
		RefCnt<T>* _Ref;//weak_ptr并不实际管理资源,只是监控资源的状态
	};
}